2010-03-30 09:55:00
灰常感谢各位达人昨天的热心回帖,让我受益匪浅。我仰望夜空,群星点点,就如各位的点睛之语,在无尽的苍穹闪耀。这让我深深地意识到,在这里,不仅可以分享成果,也可以分享困惑、分享寂寞。(开场白到此结束~)
在平常的编程中,我发现很容易遇到这种结构:
(1号方案)
1BOOL foo()
2{
3 BOOL bRet = FALSE;
4
5 HANDLE hProcess = OpenProcess(...);
6
7 if (hProcess != NULL)
8 {
9 HANDLE hToken = OpenProcessToken(hProcess, ...);
10
11 if (hToken != NULL)
12 {
13 // ...
14
15 if (LookupPrivilegeValue(...))
16 {
17 if (AdjustTokenPrivileges(hToken, ...))
18 {
19 bRet = TRUE;
20 }
21 }
22
23 CloseHandle(hToken);
24 }
25
26 CloseHandle(hProcess);
27 }
28
29 return bRet;
30}
如上写法,容易造成缩进级别不断增加。为了避免这种情况,可以改成:
(2号方案)
1BOOL foo()
2{
3 HANDLE hProcess = OpenProcess(...);
4
5 if (hProcess == NULL)
6 {
7 return FALSE;
8 }
9
10 HANDLE hToken = OpenProcessToken(hProcess, ...);
11
12 if (hToken == NULL)
13 {
14 CloseHandle(hProcess);
15
16 return FALSE;
17 }
18
19 // ...
20
21 if (!LookupPrivilegeValue(...))
22 {
23 CloseHandle(hToken);
24 CloseHandle(hProcess);
25
26 return FALSE;
27 }
28
29 if (!AdjustTokenPrivileges(hToken, ...))
30 {
31 CloseHandle(hToken);
32 CloseHandle(hProcess);
33
34 return FALSE;
35 }
36
37 CloseHandle(hToken);
38 CloseHandle(hProcess);
39
40 return TRUE;
41}
这样,又引来了新的问题,每次 return FALSE 时的清理任务比较麻烦,要是每步操作都引进新的 HANDLE 的话,后续的清理工作就变得非常繁重。有人推荐do…while(0)的结构,有人推荐goto。这两种形式分别是——
do…while(0):
(3号方案)
1BOOL foo()
2{
3 HANDLE hProcess = OpenProcess(...);
4
5 if (hProcess == NULL)
6 {
7 return FALSE;
8 }
9
10 BOOL bRet = FALSE;
11
12 do
13 {
14 HANDLE hToken = OpenProcessToken(hProcess, ...);
15
16 if (hToken == NULL)
17 {
18 break;
19 }
20
21 // ...
22
23 BOOL bRetInner = FALSE;
24
25 do
26 {
27 if (!LookupPrivilegeValue(...))
28 {
29 break;
30 }
31
32 if (!AdjustTokenPrivileges(hToken, ...))
33 {
34 break;
35 }
36
37 bRetInner = TRUE;
38
39 } while (0);
40
41 CloseHandle(hToken);
42
43 if (!bRetInner)
44 {
45 break;
46 }
47
48 bRet = TRUE;
49
50 } while (0);
51
52 CloseHandle(hProcess);
53
54 return bRet;
55}
这种结构可以避免每次 return FALSE 前的一堆清理工作,但缺点是,有几个依赖性的 HANDLE,就要嵌套几层的 do…while(0),有时候也会遇到需要三四层嵌套的情形。
goto:
(4.1号方案)
1BOOL foo()
2{
3 BOOL bRet = FALSE;
4
5 HANDLE hProcess = OpenProcess(...);
6
7 if (hProcess == NULL)
8 {
9 goto CLEAR;
10 }
11
12 HANDLE hToken = OpenProcessToken(hProcess, ...);
13
14 if (hToken == NULL)
15 {
16 goto CLEAR;
17 }
18
19 // ...
20
21 if (!LookupPrivilegeValue(...))
22 {
23 goto CLEAR;
24 }
25
26 if (!AdjustTokenPrivileges(hToken, ...))
27 {
28 goto CLEAR;
29 }
30
31 bRet = TRUE;
32
33CLEAR:
34 if (hToken != NULL)
35 {
36 CloseHandle(hToken);
37 }
38
39 if (hProcess != NULL)
40 {
41 CloseHandle(hProcess);
42 }
43
44 return bRet;
45}
(4.2号方案)
1BOOL foo()
2{
3 BOOL bRet = FALSE;
4
5 HANDLE hProcess = OpenProcess(...);
6
7 if (hProcess == NULL)
8 {
9 goto ERROR_LEVEL0;
10 }
11
12 HANDLE hToken = OpenProcessToken(hProcess, ...);
13
14 if (hToken == NULL)
15 {
16 goto ERROR_LEVEL1;
17 }
18
19 // ...
20
21 if (!LookupPrivilegeValue(...))
22 {
23 goto ERROR_LEVEL2;
24 }
25
26 if (!AdjustTokenPrivileges(hToken, ...))
27 {
28 goto ERROR_LEVEL2;
29 }
30
31 bRet = TRUE;
32
33ERROR_LEVEL2:
34 CloseHandle(hToken);
35ERROR_LEVEL1:
36 CloseHandle(hProcess);
37ERROR_LEVEL0:
38 return bRet;
39}
(4.1和4.2哪种好一点。。。?)
在这种情形下,goto 的方案似乎是完美的。但是 goto 如果遇到 C++,缺点体现出来了。下面这一段,现在是 do…while(0) 结构(只有一层嵌套,这种结构用在这里还算合理):
1BOOL foo()
2{
3 HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
4
5 while (true)
6 {
7 if (FAILED(hr))
8 {
9 break;
10 }
11
12 hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
13
14 if (FAILED(hr))
15 {
16 break;
17 }
18
19 CComPtr<IWbemLocator> pLoc = NULL;
20 hr = pLoc.CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER);
21
22 if (FAILED(hr))
23 {
24 break;
25 }
26
27 CComPtr<IWbemServices> pSvc = NULL;
28 hr = pLoc->ConnectServer(_T("ROOT\\CIMV2"), NULL, NULL, NULL, 0, NULL, NULL, &pSvc);
29
30 if (FAILED(hr))
31 {
32 break;
33 }
34
35 hr = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
36
37 if (FAILED(hr))
38 {
39 break;
40 }
41
42 CComPtr<IEnumWbemClassObject> pEnum = NULL;
43 _bstr_t bstrLang = _T("WQL");
44 _bstr_t bstrSql = _T("SELECT * FROM __InstanceCreationEvent WITHIN 10")
45 _T("WHERE TargetInstance ISA 'Win32_LogonSession' AND (TargetInstance.LogonType = 2 OR TargetInstance.LogonType = 11)");
46 hr = pSvc->ExecNotificationQuery(bstrLang, bstrSql, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnum);
47
48 if (FAILED(hr))
49 {
50 break;
51 }
52
53 ULONG uCount = 1;
54 CComPtr<IWbemClassObject> pNext = NULL;
55 hr = pEnum->Next(WBEM_INFINITE, uCount, &pNext, &uCount);
56
57 if (FAILED(hr))
58 {
59 break;
60 }
61
62 // ...
63
64 break;
65 }
66
67 CoUninitialize();
68
69 return SUCCEEDED(hr);
70}
如果改成 goto,则需要把所有需要对象的定义全放到最前面来,不然 goto 会跳过他们的初始化,编译不过。但是,所有对象都放到最前面定义,又违反了即用即声明的规则,而且太多了也容易混淆。
最后,问题是,如果遇到 C++ 的、多层嵌套的,大家一般如何组织代码呢?
谢谢!
首发:http://www.cppblog.com/Streamlet/archive/2010/03/30/110955.html